---
title: Layouts
---

Layout files can be placed in any folder inside the `./app` directory. They serve as the frame for the routes contained within that folder, and they can nest inside of each other if you place them in sub-directories.

<RouteTree
  routes={[
    { name: '_layout.tsx', description: 'Wraps all files in this directory and below', highlight: true },
    { name: 'index.tsx', description: 'Matches "/"' },
    { name: 'blog', children: [
      { name: '_layout.tsx', description: 'A custom sub-layout for all /blog pages', highlight: true },
      { name: 'index.tsx', description: 'Matches "/blog"' },
      { name: '[slug].tsx', description: 'Matches a single sub-path of "/blog", like "/blog/hello"' }
    ] },
  ]}
/>

Layouts must render one of the following to show the matched pages that exist in their directory:

- [Slot](/docs/components-Slot): Directly show sub-route with no frame.
- [Stack](/docs/components-Stack): Creates a [React Navigation Stack](https://reactnavigation.org/docs/stack-navigator/) of sub-routes.
- [Tabs](/docs/components-Tabs): Creates a [React Navigation BottomTabs](https://reactnavigation.org/docs/bottom-tab-navigator) with sub-routes.
- [Drawer](/docs/components-Drawer): Creates a [React Navigation Drawer](https://reactnavigation.org/docs/drawer-navigator) with sub-routes.

This looks something like this at the simplest:

```tsx fileName=app/_layout.tsx
import { Slot } from 'one'

export default function Layout() {
  return (
   <Slot />
  )
}
```

As of now, layouts don't support loaders. If you need to access the current route params, you can use [the useParams hook](/hooks-useParams).

## Root Layout

The root layout on web controls the entire page, from `<html>` on down. You can optionally return `html`, `body`, and `head` elements in your `app/_layout.tsx` and they will "just work" - on web, it will be output, and on native it won't so as to avoid rendering errors:

```tsx fileName=app/_layout.tsx
import { Slot } from 'one'

export default function Layout() {
  return (
    <html lang="en-US">
      <head>
        <meta charSet="utf-8" />
      </head>
      
      <body>
        <Slot />
      </body>
    </html>
  )
}
```

### useServerHeadInsertion

The root `_layout.tsx` can use a special hook that allows it to insert tags into your `<head />` tag after server rendering finishes. This is an edge-case that is mostly only useful for CSS-in-JS libraries. You can use it like so:

```tsx
import { Slot, useServerHeadInsertion } from 'one'

export default function Layout() {
  useServerHeadInsertion(() => {
    // this will run after the entire page renders
    // return a single React.ReactElement that will be spliced into your <head />
    return <style>{renderCSS()}</style>
  })

  return (
    <html>
      <Slot />
    </html>
  )
}
```

## Groups and layouts

If you'd like to logically group together some pages without creating a sub-route, you can use a group folder by naming it like so:

<RouteTree
  routes={[
    { name: '_layout.tsx', description: 'Wraps all files in this directory and below' },
    { name: 'index.tsx', description: 'Matches "/"' },
    { name: '(blog)', highlight: true, children: [
      { name: '_layout.tsx', description: 'A custom sub-layout for all /blog pages' },
      { name: 'blog.tsx', description: 'Matches "/blog"' },
      { name: '[slug].tsx', description: 'Matches a single sub-path of "/blog", like "/blog/hello"' }
    ] },
  ]}
/>

Groups let you add extra layouts. If done right, this gives you a lot of control over how your app feels.

## Nested layouts example

A common pattern that apps have is something like Twitter/X, where you have bottom tabs for your "top level" views, but then on some of the bottom tab sections, you want to have a Stack that remembers its state inside just that tab.

This pattern can be incredibly verbose to link together with React Navigation, and takes a bit of tinkering to figure out with One. Since it is common and a useful example, lets walk through how you'd build on using One's file system routing.

Here's our file structure:

<RouteTree
  routes={[
    { name: '_layout.tsx', description: 'Where our Tabs layout is defined' },
    { name: 'notifications.tsx', description: 'Matches "/notifications"' },
    { name: 'profile.tsx', description: 'Matches "/profile"' },
    { name: '(feed)', highlight: true, children: [
      { name: '_layout.tsx', description: 'Where our Stack layout is defined' },
      { name: 'index.tsx', description: 'Matches "/"' },
      { name: 'post-[id].tsx', description: 'Matches routes like "/post-123"' }
    ] },
  ]}
/>

The top level Layout will define our tabs:

```tsx fileName=app/_layout.tsx
import { Bell, Home, User } from '@tamagui/lucide-icons'
import { Home } from '~/features/icons'

export function RootLayout() {
  return (
    <Tabs
      screenOptions={{
        headerShown: false,
      }}
    >
      <Tabs.Screen
        name="(feed)"
        options={{
          title: 'Feed',
          tabBarIcon: ({ color }) => <Home size={20} color={color} />,
        }}
      />

      <Tabs.Screen
        name="notifications"
        options={{
          title: 'Notifications',
          tabBarIcon: ({ color }) => <Bell size={20} color={color} />,
        }}
      />

      <Tabs.Screen
        name="profile"
        options={{
          title: 'Profile',
          tabBarIcon: ({ color }) => <User size={20} color={color} />,
        }}
      />
    </Tabs>
  )
}
```

This will set us up with three bottom tabs: Feed, Notifications, and Profile. The Notifications and Profile tabs for now will just show their content directly, but inside of the Feed tab, we want to show a stack.

We set up the stack in `(feed)/_layout.tsx`:

```tsx fileName=(feed)/_layout.tsx
import { Slot, Stack } from 'one'

export default function FeedLayout() {
  return (
    <>
      {typeof window !== 'undefined' ? (
        <Slot />
      ) : (
        <Stack>
          <Stack.Screen name="index" options={{ title: 'Feed' }} />
          <Stack.Screen name="[id]" options={{ title: 'Post' }} />
        </Stack>
      )}
    </>
  )
}
```

One thing we're showing here is that the layout is diverging between web and native. On web, we are showing a Slot, while on Native we show a Stack. This is because browsers feel better without stacks - the native back/forward button serves as our stack controller.

On Native we are defining the configuration for each sub-screen with the `Stack.Screen` component. The Stack component is a [React Navigation native stack navigator](https://reactnavigation.org/docs/native-stack-navigator/) and nothing more, it accepts all the props you'd expect.

You'll notice we are matching the `name` to the file names names of the sub-routes, without the `.tsx` extension.

This will get you a nice Stack-inside-Tabs pattern that is common on native apps, all with just two layouts and a few routes.


## Limitations

As of today layouts don't support loaders. This is on our radar, please [chime in](https://github.com/onejs/one/discussions/376) if this is helpful for you.

Also note that `useParams` won't work in layouts, as they are never nested inside a route. Instead, you can use `useActiveParams`.
